// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/events/event_dispatcher.h"

#include <algorithm>

#include "base/macros.h"
#include "ui/events/event_target.h"
#include "ui/events/event_targeter.h"

namespace ui {

namespace {

    class ScopedDispatchHelper : public Event::DispatcherApi {
    public:
        explicit ScopedDispatchHelper(Event* event)
            : Event::DispatcherApi(event)
        {
            set_result(ui::ER_UNHANDLED);
        }

        virtual ~ScopedDispatchHelper()
        {
            set_phase(EP_POSTDISPATCH);
        }

    private:
        DISALLOW_COPY_AND_ASSIGN(ScopedDispatchHelper);
    };

} // namespace

EventDispatcherDelegate::EventDispatcherDelegate()
    : dispatcher_(NULL)
{
}

EventDispatcherDelegate::~EventDispatcherDelegate()
{
    if (dispatcher_)
        dispatcher_->OnDispatcherDelegateDestroyed();
}

Event* EventDispatcherDelegate::current_event()
{
    return dispatcher_ ? dispatcher_->current_event() : NULL;
}

EventDispatchDetails EventDispatcherDelegate::DispatchEvent(EventTarget* target,
    Event* event)
{
    CHECK(target);
    Event::DispatcherApi dispatch_helper(event);
    dispatch_helper.set_phase(EP_PREDISPATCH);
    dispatch_helper.set_result(ER_UNHANDLED);

    EventDispatchDetails details = PreDispatchEvent(target, event);
    if (!event->handled() && !details.dispatcher_destroyed && !details.target_destroyed) {
        details = DispatchEventToTarget(target, event);
    }
    bool target_destroyed_during_dispatch = details.target_destroyed;
    if (!details.dispatcher_destroyed) {
        details = PostDispatchEvent(target_destroyed_during_dispatch ? NULL : target, *event);
    }

    details.target_destroyed |= target_destroyed_during_dispatch;
    return details;
}

EventDispatchDetails EventDispatcherDelegate::PreDispatchEvent(
    EventTarget* target, Event* event)
{
    return EventDispatchDetails();
}

EventDispatchDetails EventDispatcherDelegate::PostDispatchEvent(
    EventTarget* target, const Event& event)
{
    return EventDispatchDetails();
}

EventDispatchDetails EventDispatcherDelegate::DispatchEventToTarget(
    EventTarget* target,
    Event* event)
{
    EventDispatcher* old_dispatcher = dispatcher_;
    EventDispatcher dispatcher(this);
    dispatcher_ = &dispatcher;
    dispatcher.ProcessEvent(target, event);
    if (!dispatcher.delegate_destroyed())
        dispatcher_ = old_dispatcher;
    else if (old_dispatcher)
        old_dispatcher->OnDispatcherDelegateDestroyed();

    EventDispatchDetails details;
    details.dispatcher_destroyed = dispatcher.delegate_destroyed();
    details.target_destroyed = (!details.dispatcher_destroyed && !CanDispatchToTarget(target));
    return details;
}

////////////////////////////////////////////////////////////////////////////////
// EventDispatcher:

EventDispatcher::EventDispatcher(EventDispatcherDelegate* delegate)
    : delegate_(delegate)
    , current_event_(NULL)
{
}

EventDispatcher::~EventDispatcher()
{
}

void EventDispatcher::OnHandlerDestroyed(EventHandler* handler)
{
    handler_list_.erase(std::find(handler_list_.begin(),
        handler_list_.end(),
        handler));
}

void EventDispatcher::ProcessEvent(EventTarget* target, Event* event)
{
    if (!target || !target->CanAcceptEvent(*event))
        return;

    ScopedDispatchHelper dispatch_helper(event);
    dispatch_helper.set_target(target);

    handler_list_.clear();
    target->GetPreTargetHandlers(&handler_list_);

    dispatch_helper.set_phase(EP_PRETARGET);
    DispatchEventToEventHandlers(&handler_list_, event);
    if (event->handled())
        return;

    // If the event hasn't been consumed, trigger the default handler. Note that
    // even if the event has already been handled (i.e. return result has
    // ER_HANDLED set), that means that the event should still be processed at
    // this layer, however it should not be processed in the next layer of
    // abstraction.
    if (delegate_ && delegate_->CanDispatchToTarget(target) && target->target_handler()) {
        dispatch_helper.set_phase(EP_TARGET);
        DispatchEvent(target->target_handler(), event);
        if (event->handled())
            return;
    }

    if (!delegate_ || !delegate_->CanDispatchToTarget(target))
        return;

    handler_list_.clear();
    target->GetPostTargetHandlers(&handler_list_);
    dispatch_helper.set_phase(EP_POSTTARGET);
    DispatchEventToEventHandlers(&handler_list_, event);
}

void EventDispatcher::OnDispatcherDelegateDestroyed()
{
    delegate_ = NULL;
}

////////////////////////////////////////////////////////////////////////////////
// EventDispatcher, private:

void EventDispatcher::DispatchEventToEventHandlers(EventHandlerList* list,
    Event* event)
{
    for (EventHandlerList::const_iterator it = list->begin(),
                                          end = list->end();
         it != end; ++it) {
        (*it)->dispatchers_.push(this);
    }

    while (!list->empty()) {
        EventHandler* handler = (*list->begin());
        if (delegate_ && !event->stopped_propagation())
            DispatchEvent(handler, event);

        if (!list->empty() && *list->begin() == handler) {
            // The handler has not been destroyed (because if it were, then it would
            // have been removed from the list).
            CHECK(handler->dispatchers_.top() == this);
            handler->dispatchers_.pop();
            list->erase(list->begin());
        }
    }
}

void EventDispatcher::DispatchEvent(EventHandler* handler, Event* event)
{
    // If the target has been invalidated or deleted, don't dispatch the event.
    if (!delegate_->CanDispatchToTarget(event->target())) {
        if (event->cancelable())
            event->StopPropagation();
        return;
    }

    base::AutoReset<Event*> event_reset(&current_event_, event);
    handler->OnEvent(event);
    if (!delegate_ && event->cancelable())
        event->StopPropagation();
}

} // namespace ui
